/**
 * 客户端文件模型
 *
 * Created by snowingsea on 2020/02/23.
 */
const compressing = require('compressing');
const fs = require('fs-extra');
const path = require('path');
const querystring = require('querystring');
const request = require('request-promise');
const randomstring = require('randomstring');

const testerURI = require('./../api/tester/uri');
const config = require('./../api/api-config');
const files = require('./../../phoenix/models/files');

class File {
  constructor(projectVersion, fileId, path, status, kind, shieldFilesCount, defectsCount) {
    this.projectVersion = projectVersion;
    this.fileId = fileId;
    this.path = path;
    this.status = status;
    this.kind = kind;
    this.shieldFilesCount = shieldFilesCount;
    this.defectsCount = defectsCount;
  }

  /**
   * 等待文件状态稳定
   * @return {Promise}
   */
  waiting4Updated() {
    return new Promise((resolve, reject) => {
      this.projectVersion.project.creator.login()
        .then(() => {
          const options = {
            url: `${testerURI}/files/${this.fileId}.json`,
            headers: { Authorization: `Bearer ${this.projectVersion.project.creator.token.accessToken}` },
            json: true,
          };
          return request.get(options);
        })
        .then((res) => {
          this.status = res.status;
          return this.projectVersion.project.creator.logout();
        })
        .then(() => {
          switch (this.status) {
            case files.statusKind.cloneFailed:
            case files.statusKind.downloadFailed:
            case files.statusKind.extractFailed:
            case files.statusKind.notCompiled:
              resolve();
              break;
            default:
              setTimeout(() => {
                this.waiting4Updated().then(resolve).catch(reject);
              }, 3000);
              break;
          }
        })
        .catch(reject);
    });
  }

  /**
   * 获取文件
   * @param {String} fileId 文件id
   * @return {Promise}
   */
  getFile(fileId) {
    return new Promise((resolve, reject) => {
      let file;
      this.projectVersion.project.creator.login()
        .then(() => {
          const options = {
            url: `${testerURI}/files/${fileId}.json?`,
            headers: { Authorization: `Bearer ${this.projectVersion.project.creator.token.accessToken}` },
            json: true,
          };
          return request.get(options);
        })
        .then(({
          path, status, kind, shieldFilesCount, defectsCount,
        }) => {
          file = new File(this.projectVersion, fileId, path, status, kind, shieldFilesCount, defectsCount);
          return this.projectVersion.project.creator.logout();
        })
        .then(() => {
          resolve(file);
        })
        .catch(reject);
    });
  }

  /**
   * 获取文件类型系统
   * @param {String} fileId 文件id
   * @param {String} accessToken 访问令牌
   * @return {Promise}
   */
  getFileTypes(fileId, accessToken = undefined) {
    return new Promise((resolve, reject) => {
      this.projectVersion.project.creator.token.accessToken = accessToken;
      let types;
      Promise.resolve()
        .then(() => {
          if (!accessToken) {
            return this.projectVersion.project.creator.login();
          }
          return Promise.resolve();
        })
        .then(() => {
          const options = {
            url: `${testerURI}/files/${fileId}/types.json?`,
            headers: { Authorization: `Bearer ${this.projectVersion.project.creator.token.accessToken}` },
            json: true,
          };
          return request.get(options);
        })
        .then((res) => {
          types = res.types;
          if (!accessToken) {
            return this.projectVersion.project.creator.logout();
          }
          return Promise.resolve();
        })
        .then(() => {
          resolve(types);
        })
        .catch(reject);
    });
  }

  /**
   * 获取文件树
   * @return {Promise}
   */
  getTree(extensions = undefined) {
    return new Promise((resolve, reject) => {
      let tree;
      this.projectVersion.project.creator.login()
        .then(() => {
          const query = extensions ? querystring.stringify({ extensions }) : '';
          const options = {
            url: `${testerURI}/files/${this.fileId}/tree.json?${query}`,
            headers: { Authorization: `Bearer ${this.projectVersion.project.creator.token.accessToken}` },
            json: true,
          };
          return request.get(options);
        })
        .then((res) => {
          tree = res;
          return this.projectVersion.project.creator.logout();
        })
        .then(() => {
          resolve(tree);
        })
        .catch(reject);
    });
  }

  /**
   * 获取文件树，递归获取所有子节点
   * @return {Promise}
   */
  getTreeAll(extensions = undefined) {
    return new Promise((resolve, reject) => {
      let tree;
      this.projectVersion.project.creator.login()
        .then(() => {
          const query = extensions ? querystring.stringify({ extensions }) : '';
          const options = {
            url: `${testerURI}/files/${this.fileId}/tree-all.json?${query}`,
            headers: { Authorization: `Bearer ${this.projectVersion.project.creator.token.accessToken}` },
            json: true,
          };
          return request.get(options);
        })
        .then((res) => {
          tree = res;
          return this.projectVersion.project.creator.logout();
        })
        .then(() => {
          resolve(tree);
        })
        .catch(reject);
    });
  }

  /**
   * 重置文件/目录
   * @param {*} fileId
   */
  reset() {
    return new Promise((resolve, reject) => {
      let tree;
      this.projectVersion.project.creator.login()
        .then(() => {
          const options = {
            url: `${testerURI}/files/${this.fileId}/reset.json`,
            headers: { Authorization: `Bearer ${this.projectVersion.project.creator.token.accessToken}` },
            json: true,
          };
          return request.post(options);
        })
        .then((res) => {
          tree = res;
          return this.projectVersion.project.creator.logout();
        })
        .then(() => {
          resolve(tree);
        })
        .catch(reject);
    });
  }

  /**
   * 创建文件/文件夹
   * @param {Number} kind 文件类型
   * @return {Promise}
   *
   */
  createEmptyFile(kind = files.kind.file) {
    return new Promise((resolve, reject) => {
      let file;
      this.projectVersion.project.creator.login()
        .then(() => {
          const fileName = randomstring.generate(8);
          const options = {
            url: `${testerURI}/files/${this.fileId}/file.json?${querystring.stringify({ kind, fileName })}`,
            headers: { Authorization: `Bearer ${this.projectVersion.project.creator.token.accessToken}` },
            json: true,
          };
          return request.post(options);
        })
        .then((res) => {
          file = new File(this.projectVersion, res.fileId, res.path, res.status, res.kind);
          return this.projectVersion.project.creator.logout();
        })
        .then(() => {
          resolve(file);
        })
        .catch(reject);
    });
  }

  /**
   * 删除文件/文件夹
   * @param {String} fileId 文件id
   * @return {Promise}
   */
  removeFile(fileId) {
    return new Promise((resolve, reject) => {
      this.projectVersion.project.creator.login()
        .then(() => {
          const options = {
            url: `${testerURI}/files/${fileId}.json`,
            headers: { Authorization: `Bearer ${this.projectVersion.project.creator.token.accessToken}` },
            json: true,
          };
          return request.delete(options);
        })
        .then(() => this.projectVersion.project.creator.logout())
        .then(resolve)
        .catch(reject);
    });
  }

  /**
   * 更新文件内容
   * @param {String} content 文件内容
   * @return {Promise}
   */
  updateContent(content = randomstring.generate(100)) {
    return new Promise((resolve, reject) => {
      this.projectVersion.project.creator.login()
        .then(() => {
          const options = {
            url: `${testerURI}/files/${this.fileId}.json`,
            headers: { Authorization: `Bearer ${this.projectVersion.project.creator.token.accessToken}` },
            form: { content },
            json: true,
          };
          return request.put(options);
        })
        .then(() => this.projectVersion.project.creator.logout())
        .then(resolve)
        .catch(reject);
    });
  }

  /**
   * 随机更新文件
   * @param {String} homeDir 根文件夹路径
   * @param {String} packagePath 压缩包路径
   * @param {Array} extNames 后缀列表
   * @return {Promise}
   */
  randomUpdate({ homeDir = undefined, packagePath = undefined, extNames = undefined } = {}) {
    return new Promise((resolve, reject) => {
      let info;
      File.createRandomPackage('zip', homeDir, packagePath, extNames)
        .then((res) => {
          info = res;
          const options = {
            url: `${testerURI}/files/${this.fileId}/file.json?${querystring.stringify({ type: files.repositoryType.file })}`,
            headers: { Authorization: `Bearer ${this.projectVersion.project.creator.token.accessToken}` },
            json: true,
            formData: {
              file: fs.createReadStream(res.packagePath),
            },
          };
          return request.put(options);
        })
        .then(() => {
          resolve(info);
        })
        .catch(reject);
    });
  }

  /**
   * 解析源码
   * @return {Promise}
   */
  analyse() {
    return new Promise((resolve, reject) => {
      const { projectVersion } = this;
      const { versionId } = projectVersion;
      const query = { fileIds: [this.fileId] };
      const options = {
        url: `${testerURI}/project-versions/${versionId}/files.json?${querystring.stringify(query)}`,
        headers: { Authorization: `Bearer ${projectVersion.project.creator.token.accessToken}` },
        json: true,
      };
      request.post(options)
        .then(() => projectVersion.project.creator.waiting4TasksOfVersionFinished(versionId))
        .then(resolve)
        .catch(reject);
    });
  }

  /**
   * 静态分析
   * @return {Promise}
   */
  staticAnalyze(params) {
    return new Promise((resolve, reject) => {
      const { projectVersion } = this;
      const { versionId } = projectVersion;
      const body = params || { fileIds: [this.fileId] };
      const options = {
        url: `${testerURI}/project-versions/${versionId}/static-analyze.json`,
        headers: { Authorization: `Bearer ${projectVersion.project.creator.token.accessToken}` },
        body,
        json: true,
        simple: false,
      };
      request.post(options, (err, res) => {
        if (err) {
          reject(err);
        }
        resolve(res);
      });
    });
  }

  /**
   * 生成测试用例
   * @param {Array} functionIdArray 选中函数列表
   */
  generateTests(functionIdArray = undefined) {
    return new Promise((resolve, reject) => {
      const { projectVersion } = this;
      const { versionId } = projectVersion;
      const query = functionIdArray ? { functionIds: functionIdArray } : { fileIds: [this.fileId] };
      const options = {
        url: `${testerURI}/project-versions/${versionId}/tests.json?${querystring.stringify(query)}`,
        headers: { Authorization: `Bearer ${projectVersion.project.creator.token.accessToken}` },
        json: true,
      };
      request.post(options)
        .then(() => projectVersion.project.creator.waiting4TasksOfVersionFinished(versionId))
        .then(resolve)
        .catch(reject);
    });
  }

  /**
   * 创建随机目录
   * @param {String} dir 当前目录
   * @param {Array} extNames 后缀列表
   * @param {Number} deep 深度
   * @return {Promise}
   */
  static createRandomDirectory(dir, extNames, deep) {
    if (deep <= 0) {
      return Promise.resolve();
    }

    return new Promise((resolve, reject) => {
      const promises = [];
      const subdirs = [];
      for (let i = Math.floor(Math.random() * 10); i > 0; i -= 1) {
        const filePath = path.join(dir, randomstring.generate(5));
        const random = Math.random();
        if (random < 0.25) {
          promises.push(fs.ensureFile(filePath));
        } else if (random < 0.6) {
          const extName = extNames[Math.floor(Math.random() * extNames.length)];
          promises.push(fs.ensureFile(`${filePath}${extName}`));
        } else {
          subdirs.push(filePath);
        }
      }
      Promise.all(promises)
        .then(() => {
          const promises = subdirs.map((subdir) => {
            const next = deep - 1;
            return File.createRandomDirectory(subdir, extNames, next);
          });
          return Promise.all(promises);
        })
        .then(resolve)
        .catch(reject);
    });
  }

  /**
   * 创建随机压缩包文件
   * @param {String} type 压缩文件类型
   * @param {String} homeDir 根文件夹路径
   * @param {String} packagePath 压缩包路径
   * @param {Array} extNames 后缀列表
   * @return {Promise<Object>}
   */
  static createRandomPackage(type = 'zip', homeDir = undefined, packagePath = undefined, extNames = undefined) {
    return new Promise((resolve, reject) => {
      if (!homeDir) {
        homeDir = path.join(config.path.cacheDir, randomstring.generate(16));
      }
      if (!packagePath) {
        packagePath = `${homeDir}.${type}`;
      }
      if (!extNames) {
        extNames = [];
        for (let i = Math.floor(Math.random() * 4) + 4; i > 0; i -= 1) {
          extNames.push(`.${randomstring.generate(3)}`);
        }
      }
      fs.remove(homeDir)
        .then(fs.ensureDir(homeDir))
        .then(() => {
          const deep = Math.floor(Math.random() * 4) + 4;
          return File.createRandomDirectory(homeDir, extNames, deep);
        })
        .then(() => fs.remove(packagePath))
        .then(() => compressing[type].compressDir(homeDir, packagePath))
        .then(() => {
          resolve({ homeDir, packagePath, extNames });
        })
        .catch(reject);
    });
  }
}

/**
 * Export model definition object.
 */
module.exports = File;
